package utils;


import java.util.Random;

public class Vector3
{

    public Vector3()
    {
        setZero();
    }

    public Vector3(float x, float y, float z)
    {
        set(x, y, z);
    }

    public void set(Vector3 that)
    {
        x = that.x;
        y = that.y;
        z = that.z;
    }

    public void set(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public void setZero()
    {
        set(0.0F, 0.0F, 0.0F);
    }

    public boolean equals(Vector3 that)
    {
        return x == that.x && y == that.y && z == that.z;
    }

    public boolean equalsZero()
    {
        return x == 0.0F && y == 0.0F && z == 0.0F;
    }

    public float magnitude()
    {
        return (float)Math.sqrt(magnitudeSquared());
    }

    public float magnitudeSquared()
    {
        return x * x + y * y + z * z;
    }

    public float dot(Vector3 that)
    {
        return x * that.x + y * that.y + z * that.z;
    }

    public void setUnitRandom()
    {
        do
        {
            x = generator.nextFloat() * 2.0F - 1.0F;
            y = generator.nextFloat() * 2.0F - 1.0F;
            z = generator.nextFloat() * 2.0F - 1.0F;
        } while(magnitudeSquared() > 1.0F);
    }

    public float approximateLength()
    {
        float a = x;
        if(a < 0.0F)
            a = -a;
        float b = y;
        if(b < 0.0F)
            b = -b;
        float c = z;
        if(c < 0.0F)
            c = -c;
        if(a < b)
        {
            float t = a;
            a = b;
            b = t;
        }
        if(a < c)
        {
            float t = a;
            a = c;
            c = t;
        }
        return a * 0.9375F + (b + c) * 0.375F;
    }

    public float distance(Vector3 v)
    {
        synchronized(distTemp)
        {
            distTemp.setDiff(this, v);
            float f = distTemp.magnitude();
            return f;
        }
    }

    public float approximateDistance(Vector3 v)
    {
        synchronized(distTemp)
        {
            distTemp.setDiff(this, v);
            float f = distTemp.approximateLength();
            return f;
        }
    }

    public void setSum(Vector3 a, Vector3 b)
    {
        x = a.x + b.x;
        y = a.y + b.y;
        z = a.z + b.z;
    }

    public void setDiff(Vector3 a, Vector3 b)
    {
        x = a.x - b.x;
        y = a.y - b.y;
        z = a.z - b.z;
    }

    public void setScale(float a, Vector3 b)
    {
        x = a * b.x;
        y = a * b.y;
        z = a * b.z;
    }

    public void setCross(Vector3 a, Vector3 b)
    {
        set(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
    }

    public void setInterp(float blend, Vector3 v0, Vector3 v1)
    {
        x = v0.x + blend * (v1.x - v0.x);
        y = v0.y + blend * (v1.y - v0.y);
        z = v0.z + blend * (v1.z - v0.z);
    }

    public void setNormalize()
    {
        float m = magnitude();
        if(m != 0.0F)
            setScale(1.0F / m, this);
    }

    public void setApproximateNormalize()
    {
        float m = approximateLength();
        if(m != 0.0F)
            setScale(1.0F / m, this);
    }

    public void setApproximateTruncate(float threshold)
    {
        float length = approximateLength();
        if(length > threshold)
            setScale(threshold / length, this);
    }
    
    
    
    public static void aEqualsBPlusC(Vector3 a, Vector3 b, Vector3 c)
    {
        a.x = b.x + c.x;
        a.y = b.y + c.y;
        a.z = b.z + c.z;
    }

    public static void aEqualsBMinusC(Vector3 a, Vector3 b, Vector3 c)
    {
        a.x = b.x - c.x;
        a.y = b.y - c.y;
        a.z = b.z - c.z;
    }

    public static void aEqualsBTimesC(Vector3 a, float b, Vector3 c)
    {
        a.x = b * c.x;
        a.y = b * c.y;
        a.z = b * c.z;
    }

    public static void aEqualsBCrossC(Vector3 a, Vector3 b, Vector3 c)
    {
        a.set(b.y * c.z - b.z * c.y, b.z * c.x - b.x * c.z, b.x * c.y - b.y * c.x);
    }

    public Vector3 scale(float factor)
    {
        return new Vector3(x * factor, y * factor, z * factor);
    }

    
    public Vector3 add(Vector3 other)
    {
        return new Vector3(x + other.x, y + other.y, z + other.z);
    }

    public Vector3 subtract(Vector3 other)
    {
        return new Vector3(x - other.x, y - other.y, z - other.z);
    }

    public void normalizeInPlace()
    {
        float m = magnitude();
        if(m != 0.0F)
            aEqualsBTimesC(this, 1.0F / m, this);
    }

    public void approximateNormalizeInPlace()
    {
        float m = approximateLength();
        if(m != 0.0F)
            aEqualsBTimesC(this, 1.0F / m, this);
    }

    public void approximateTruncateInPlace(float threshold)
    {
        float length = approximateLength();
        if(length > threshold)
            aEqualsBTimesC(this, threshold / length, this);
    }

    
    public static Vector3 unitRandom()
    {
        Vector3 v = new Vector3(0.0F, 0.0F, 0.0F);
        do
        {
            v.x = generator.nextFloat() * 2.0F - 1.0F;
            v.y = generator.nextFloat() * 2.0F - 1.0F;
            v.z = generator.nextFloat() * 2.0F - 1.0F;
        } while(v.magnitudeSquared() > 1.0F);
        return v;
    }


    public float x;
    public float y;
    public float z;
    public static final Vector3 zero = new Vector3();
    public static Random generator = new Random();
    private static Vector3 distTemp = new Vector3();
    
    
    
    
    

}
